package com.adkun.community.service;

import com.adkun.community.dao.LoginTicketMapper;
import com.adkun.community.dao.UserMapper;
import com.adkun.community.entity.LoginTicket;
import com.adkun.community.entity.User;
import com.adkun.community.util.CommunityConstants;
import com.adkun.community.util.CommunityUtil;
import com.adkun.community.util.MailClient;
import com.adkun.community.util.RedisKeyUtil;
import io.netty.util.internal.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.PushbackReader;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class UserService implements CommunityConstants {

    @Autowired
    private UserMapper userMapper;

    // 注册要发邮件，所以要把邮件客户端、模板引擎注入
    @Autowired
    private MailClient mailClient;

    @Autowired
    private TemplateEngine templateEngine;

    //发邮件生成激活码，要包含域名、项目名
    @Value("${community.path.domain}")
    private String domain;

    @Value("${server.servlet.context-path}")
    private String contextPath;

    /**
     * 已Redis重构获取User
     *
     * @param id
     * @return
     */
    public User findUserById(int id) {
        User user = getCache(id);
        if (user == null) {
            user = initCache(id);
        }
        return user;
    }

    /*
    优先从缓存中取
    */
    private User getCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }

    /*
    取不到初始化缓存数据
    */
    private User initCache(int userId) {
        // 从mysql把数据查到
        User user = userMapper.selectById(userId);
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
        return user;
    }

    /*
    数据更新后清除缓存
     */
    private void clearCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);
    }

    /**
     * 使用用户名查询User
     *
     * @param name
     * @return
     */
    public User findUserByName(String name) {
        return userMapper.selectByName(name);
    }

    /**
     * 使用email查找 User
     *
     * @param email
     * @return
     */
    public User findUserByEmail(String email) {
        return userMapper.selectByEmail(email);
    }


    /**
     * 注册
     *
     * @param user
     * @return
     */
    public Map<String, Object> register(User user) {
        // 由于返回的有多种情况：邮箱已存在，用户名已存在等，所以返回值用Map
        Map<String, Object> map = new HashMap<>();

        // 对空值判断处理
        if (user == null) {
            throw new IllegalArgumentException("参数不能为空");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernameMsg", "帐号不能为空!");
            return map;
        }
        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密码不能为空！");
            return map;
        }
        if (StringUtils.isBlank(user.getEmail())) {
            map.put("emailMsg", "邮箱不能为空！");
            return map;
        }
        if (user.getPassword().length() < 8) {
            map.put("passwordMsg", "密码不能小于8位！");
            return map;
        }

        // 判断帐号是否已注册，邮箱是否已注册
        User u = userMapper.selectByName(user.getUsername());
        if (u != null) {
            map.put("usernameMsg", "用户名已存在！");
            return map;
        }

        u = userMapper.selectByEmail(user.getEmail());
        if (u != null) {
            map.put("emailMsg", "邮箱已被注册！");
            return map;
        }

        // 注册用户
        // 密码加密
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
        user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));
        user.setType(0);
        user.setStatus(0);
        user.setActivationCode(CommunityUtil.generateUUID());
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setCreateTime(new Date());
        // 添加用户到库
        userMapper.insertUser(user);

        // 发送激活邮件
        Context context = new Context();
        context.setVariable("email", user.getEmail());
        // http://localhost:8081/community/activation/101/code
        // user传进来没有id，调用了userMapper.insertUser()就有ID了
        // 因为配置了mybatis.configuration.useGeneratedKeys=true，并且在mapper.xml里面指定了id=#{id}，所以insert时会回填
        String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
        context.setVariable("url", url);
        String content = templateEngine.process("/mail/activation", context);

        mailClient.sendMail(user.getEmail(), "激活您的帐号", content);


        return null;
    }

    /**
     * 激活
     *
     * @param user
     * @return
     */
    public int activate(User user) {
        User u = userMapper.selectById(user.getId());
        if (user.getActivationCode().equals(u.getActivationCode())) {
            // 激活码正确
            if (u.getStatus() == 1) {
                // 重复激活
                return ACTIVATION_REPEAT;
            } else {
                userMapper.updateStatus(user.getId(), 1);
                clearCache(user.getId());
                return ACTIVATION_SUCCESS;
            }
        } else {
            return ACTIVATION_FAILURE;
        }
    }

    @Autowired
    private RedisTemplate redisTemplate;


//    @Autowired
//    LoginTicketMapper ticketMapper;

    /**
     * 登录
     *
     * @param user
     * @param expiredSeconds
     * @return
     */
    public Map<String, Object> login(User user, int expiredSeconds) {
        Map<String, Object> map = new HashMap<>();

        // 空值处理
        if (user == null) {
            throw new IllegalArgumentException("参数不能为空");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernamgMsg", "帐号不能为空！");
            return map;
        }
        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密码不能为空！");
            return map;
        }

        // 验证帐号
        User u = userMapper.selectByName(user.getUsername());
        if (u == null) {
            map.put("usernameMsg", "该帐号不存在！");
            return map;
        }
        // 验证状态
        if (u.getStatus() == 0) {
            map.put("usernameMsg", "该帐号未激活！");
            return map;
        }
        // 验证密码
        String password = CommunityUtil.md5(user.getPassword() + u.getSalt());
        if (!u.getPassword().equals(password)) {
            // 如果密码不相等
            map.put("passwordMsg", "密码不正确！");
            return map;
        }

        // 生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(u.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
//        ticketMapper.insertLoginTicket(loginTicket);

        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        redisTemplate.opsForValue().set(redisKey, loginTicket);

        map.put("ticket", loginTicket.getTicket());

        return map;
    }

    /**
     * 登出
     *
     * @param ticket
     */
    public void logout(String ticket) {
        // 由ticket找到登录凭证
//        ticketMapper.updateStatus(ticket, 1);
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        loginTicket.setStatus(1);
        redisTemplate.opsForValue().set(redisKey, loginTicket);
    }

    public Map<String, Object> sendCode(String email) {
        Map<String, Object> ret = new HashMap<>();
        if (email == null || StringUtils.isBlank(email)) {
            ret.put("emailMsg", "邮箱不能为空！");
            return ret;
        }
        User user = userMapper.selectByEmail(email);
        if (user == null) {
            ret.put("emailMsg", "邮箱用户不存在！");
            return ret;
        }
        // 判断Redis中是否已经有邮件了
        String k = (String) redisTemplate.opsForValue().get(RedisKeyUtil.getModifyKey(email));
        if (k != null) {
            ret.put("kaptchaMsg", "验证码已发送，如未收到，请等待1分钟后重发");
            return ret;
        }
        // 用户存在，发送邮件
        Context context = new Context();
        String kaptcha = CommunityUtil.generateUUID().substring(0, 4);
        String content = "您的验证码是：" + kaptcha + "\n   1分钟内有效";
        String title = "忘记密码 - 验证码";
        mailClient.sendMail(user.getEmail(), title, content);
        // 将验证码存到 Redis email:kaptcha
        redisTemplate.opsForValue().set(RedisKeyUtil.getModifyKey(email), kaptcha, 1, TimeUnit.MINUTES);
        return null;
    }


    /**
     * 忘记密码
     *
     * @param email
     * @param newPassword
     * @return
     */
    public Map<String, Object> forget(String email, String newPassword, String kaptcha) {
        Map<String, Object> ret = new HashMap<>();
        if (email == null || StringUtils.isBlank(email)) {
            ret.put("emailMsg", "邮箱不能为空！");
            return ret;
        }
        if (newPassword == null || StringUtils.isBlank(newPassword)) {
            ret.put("newPasswordMsg", "密码不能为空！");
            return ret;
        }
        if (kaptcha == null || StringUtils.isBlank(kaptcha)) {
            ret.put("kaptchaMsg", "验证码不能为空！");
            return ret;
        }
        String storedKaptcha = (String) redisTemplate.opsForValue().get(RedisKeyUtil.getModifyKey(email));
        if (storedKaptcha == null) {
            ret.put("kaptchaMsg", "验证码未发送或已过期！");
            return ret;
        } else if (!storedKaptcha.equals(kaptcha)) {
            // 验证码不正确
            ret.put("kaptchaMsg", "验证码不正确！");
            return ret;
        }
        // 验证完成，修改密码
        User user = userMapper.selectByEmail(email);
        if (user == null) {
            ret.put("emailMsg", "邮箱不正确！");
            return ret;
        }
        newPassword = CommunityUtil.md5(newPassword + user.getSalt());
        userMapper.updatePassword(user.getId(), newPassword);
        return null;
    }

    public LoginTicket findLoginTicket(String ticket) {
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        return (LoginTicket) redisTemplate.opsForValue().get(redisKey);
    }

    /**
     * 修改头像
     *
     * @param userId
     * @param headerUrl
     * @return
     */
    public int updateHeader(int userId, String headerUrl) {
        int res = userMapper.updateHeader(userId, headerUrl);
        clearCache(userId);
        return res;
    }

    /**
     * 修改密码
     *
     * @param userId
     * @param
     * @return
     */

    public Map<String, Object> updatePassword(int userId, String oldPassword, String newPassword) {
        Map<String, Object> map = new HashMap<>();
        User u = userMapper.selectById(userId);
        String encOldPassword = CommunityUtil.md5(oldPassword + u.getSalt());

        if (u == null) {
            map.put("oldPasswordMsg", "出错");
            return map;
        }
        if (!encOldPassword.equals(u.getPassword())) {
            map.put("oldPasswordMsg", "密码不正确！");
            return map;
        }
//        if (!oldPassword.equals(newPassword)) {
//            map.put("confirmPasswordMsg", "两次输入密码不一致！");
//            return map;
//        }

        String np = newPassword + u.getSalt();
        np = CommunityUtil.md5(np);  // 新的加密密码

        userMapper.updatePassword(u.getId(), np);
        clearCache(u.getId());
        return null;
    }

    /**
     * 获取用户权限
     * @param userId
     * @return
     */
    public Collection<? extends GrantedAuthority> getAuthorities(int userId) {
        User user = this.findUserById(userId);

        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (user.getType()) {
                    case 1:
                        return AUTHORITY_ADMIN;
                    case 2:
                        return AUTHORITY_MODERATOR;
                    default:
                        return AUTHORITY_USER;
                }
            }
        });
        return list;
    }

}
